/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package uuid for common functions
package uuid

import (
	"crypto/rand"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"hash"
	"io"
	"strings"
)

const (
	defaultByteNum         = 16
	indexFour              = 4
	indexSix               = 6
	indexEight             = 8
	indexNine              = 9
	indexTen               = 10
	indexThirteen          = 13
	indexFourteen          = 14
	indexEighteen          = 18
	indexNineteen          = 19
	indexTwentyThree       = 23
	indexTwentyFour        = 24
	indexThirtySix         = 36
	defaultSHA1BaseVersion = 5
)

// RandomUUID -
type RandomUUID [defaultByteNum]byte

var (
	rander = rand.Reader // random function
	// NameSpaceURL Well known namespace IDs and UUIDs
	NameSpaceURL, _ = parseUUID("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
)

// New -
func New() RandomUUID {
	return mustUUID(newRandom())
}

func mustUUID(uuid RandomUUID, err error) RandomUUID {
	if err != nil {
		return RandomUUID{}
	}
	return uuid
}

func newRandom() (RandomUUID, error) {
	return newRandomFromReader(rander)
}

func newRandomFromReader(r io.Reader) (RandomUUID, error) {
	var randomUUID RandomUUID
	_, err := io.ReadFull(r, randomUUID[:])
	if err != nil {
		return RandomUUID{}, err
	}
	randomUUID[indexSix] = (randomUUID[indexSix] & 0x0f) | 0x40     // Version 4
	randomUUID[indexEight] = (randomUUID[indexEight] & 0x3f) | 0x80 // Variant is 10
	return randomUUID, nil
}

// String-
func (uuid RandomUUID) String() string {
	var buf [indexThirtySix]byte
	encodeHex(buf[:], uuid)
	return string(buf[:])
}

func encodeHex(dstBuf []byte, uuid RandomUUID) {
	hex.Encode(dstBuf, uuid[:indexFour])
	dstBuf[indexEight] = '-'
	hex.Encode(dstBuf[indexNine:indexThirteen], uuid[indexFour:indexSix])
	dstBuf[indexThirteen] = '-'
	hex.Encode(dstBuf[indexFourteen:indexEighteen], uuid[indexSix:indexEight])
	dstBuf[indexEighteen] = '-'
	hex.Encode(dstBuf[indexNineteen:indexTwentyThree], uuid[indexEight:indexTen])
	dstBuf[indexTwentyThree] = '-'
	hex.Encode(dstBuf[indexTwentyFour:], uuid[indexTen:])
}

// NewSHA1 -
func NewSHA1(space RandomUUID, data []byte) RandomUUID {
	return NewHash(sha1.New(), space, data, defaultSHA1BaseVersion)
}

// NewHash returns a new RandomUUID derived from the hash of space concatenated with
// data generated by h.  The hash should be at least 16 byte in length.  The
// first 16 bytes of the hash are used to form the RandomUUID.
func NewHash(sha1Hash hash.Hash, space RandomUUID, data []byte, version int) RandomUUID {
	sha1Hash.Reset()
	if _, err := sha1Hash.Write(space[:]); err != nil {
		return RandomUUID{}
	}
	if _, err := sha1Hash.Write(data); err != nil {
		return RandomUUID{}
	}
	s := sha1Hash.Sum(nil)
	var uuid RandomUUID
	copy(uuid[:], s)
	// Set the version bits in the RandomUUID.
	uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) // The version bits are located at positions 13-15.
	// Set the variant bits in the RandomUUID.
	uuid[8] = (uuid[8] & 0x3f) | 0x80 // The variant bits are located at positions 8-11 (counting from 0).
	return uuid
}

func parseUUID(uuidStr string) (RandomUUID, error) {
	const separator = "-"

	uuidStr = strings.ReplaceAll(uuidStr, separator, "")

	if len(uuidStr) != 32 { // Check if the length of the RandomUUID string is exactly 32 characters (16 bytes).
		return RandomUUID{}, errors.New("invalid RandomUUID length")
	}

	part1, part2 := uuidStr[:16], uuidStr[16:] // Split the RandomUUID string into two parts, each representing 8 bytes.

	b1, err := hex.DecodeString(part1)
	if err != nil {
		return RandomUUID{}, err
	}
	b2, err := hex.DecodeString(part2)
	if err != nil {
		return RandomUUID{}, err
	}

	var uuid RandomUUID
	copy(uuid[:8], b1) // Copy the first 8 bytes into the RandomUUID variable.
	copy(uuid[8:], b2) // Copy the remaining 8 bytes into the RandomUUID variable.

	return uuid, nil
}
